using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using NovodexWrapper;

namespace marbletrack
{
    public abstract class TrackPiece
    {
        public Vector3 modelRotation_;
        public float modelScale_ = 1f;
        private Vector3 modelPosition_ = new Vector3(Game1.GRIDBOUNDRY, -2 * Game1.GRIDBOUNDRY, Game1.GRIDBOUNDRY);
        protected ModelClass model_;
        public TrackPiece Next_ = null;
        public TrackPiece Prev_ = null;
        public Support support = null;

        protected Matrix transformMatrix_;

        public Vector3 exitPoint_;
        public Vector3 exitDirection_;
        public Vector3 directionFromWest = new Vector3(0, MathHelper.ToRadians(180), 0);
        public Vector3 directionFromNorth = new Vector3(0, MathHelper.ToRadians(90), 0);
        public Vector3 directionFromEast = Vector3.Zero;
        public Vector3 directionFromSouth = new Vector3(0, MathHelper.ToRadians(270), 0);

        //Orientation Matrices used by contact reports to check the orientation of an upSlope piece (for calculation boost direction)
        public static readonly Matrix StraightOrientation = new Matrix(0.0000000596046448f, 0.0f, 1.0f, 0 , 0.0f, 1.0f, 0.0f, 0, -1.0f, 0.0f, 0.0000000596046448f, 0, 0, 0, 0, 1);
        public static readonly Matrix LeftOrientation = new Matrix(1.0f, 0.0f, -0.000000174845553f, 0, 0.0f, 1.0f, 0.0f, 0, 0.000000174845553f, 0.0f, 1.0f, 0, 0, 0, 0, 1);
        public static readonly Matrix RightOrientation = new Matrix(-1.0f, 0.0f, 0.00000008742278f, 0, 0.0f, 1.0f, 0.0f, 0, -0.00000008742278f, 0.0f, -1.0f, 0, 0, 0, 0, 1);
        public static readonly Matrix BackOrientation = new Matrix(-0.00000011920929f, 0.0f, -1.0f, 0, 0.0f, 1.0f, 0.0f, 0, 1.0f, 0.0f, -0.00000011920929f, 0, 0, 0, 0, 1);

        public Matrix CorrectPosition_ = Matrix.Identity;

        //Physics Variables
        protected Physics physics;
        protected NxActor trackActor_;
        public NxActor Actor
        {
            get { return trackActor_; }
        }
        
        public TrackPiece()
        {
            modelPosition_ = Vector3.Zero;
            physics = Game1.GetPhysics;
        }

        protected abstract void SetupVariables(Vector3 rotation, Vector3 modelPosition);

        //used so matrix used for drawing is updated when model position is changed
        public Vector3 ModelPosition
        {
            get
            {
                return modelPosition_;
            }
            set
            {
                modelPosition_ = value;
                ApplyTransformations();
            }
        }

        public void Update()
        {
            //Update the physics actor's position with the position of the model
            trackActor_.GlobalPose = MathConversion.ToNxMat34(transformMatrix_);
        }

        //Create a transformation matrix that converts from Object Space -> World Space
        public void ApplyTransformations()
        {
            Matrix rotationMatrix = Matrix.CreateFromYawPitchRoll(
                modelRotation_.Y,
                modelRotation_.X,
                modelRotation_.Z);
            Matrix scaleMatrix = Matrix.CreateScale(modelScale_);
            Matrix translationMatrix = Matrix.CreateTranslation(modelPosition_);
            transformMatrix_ = CorrectPosition_ * (rotationMatrix * scaleMatrix * translationMatrix);
        }

        public void Draw()
        {
            model_.Draw(transformMatrix_);
        }
        #region Old Saver Method (Saver 1)
        /*
        public List<string> ToStringList()
        {
            List<string> savedStuff = new List<string>();
            savedStuff.Add(this.ToString());
            savedStuff.Add(modelRotation_.X.ToString());
            savedStuff.Add(modelRotation_.Y.ToString());
            savedStuff.Add(modelRotation_.Z.ToString());
            savedStuff.Add(modelScale_.ToString());
            savedStuff.Add(nextModelPosition_.X.ToString());
            savedStuff.Add(nextModelPosition_.Y.ToString());
            savedStuff.Add(nextModelPosition_.Z.ToString());

            return savedStuff;
        }*/
        #endregion

        public abstract void SetEntryExitPoints();

        //Gets a bounding box (AABB) for the trackpiece and checks it against all actor's AABB's in the scene for overlaps
        public bool CheckOverlap()
        {
            NxBounds3 worldBounds = trackActor_.getFirstShape().getWorldBounds();
            worldBounds.fatten(-0.1f); //shrink the bounding box by a small amount so collisions aren't reported for objects which are right next to each other
            foreach (TrackPiece trackPiece in Game1.modelList)
            {
                foreach (NxShape shape in trackPiece.trackActor_.getShapes())
                {
                    if (shape.checkOverlapAABB(worldBounds))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
    }
}
